
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include <cstring>
#include <iostream>
using namespace std;

#define NUM 1034  // 保存完整的命令行字符串的大小
#define SIZE 32   // 保存打散后命令字符串子串的数组大小
#define SEP " "   // 作为分隔符

char *g_argv[SIZE];  // 用于保存打散后的命令行字符串子串
char cmd_line[NUM];  // 缓冲区   - 用于保存完整的命令行字符串
char g_myval[64];

// shell 运行原理 ： 子进程执行命令，父进程等待以及解析命令

int main() {
  //   cout << "hello world" << endl;
  /*
     命令行解释器是一个常驻进程,一般情况常驻进程不退出
  */
  while (true) {
    // 1.打印出提示信息
    printf("[SilverChariot@local MyShell]# ");
    fflush(stdout);
    memset(cmd_line, '\0', sizeof cmd_line);  // 将缓冲区进行初始化
    // 2.获取用户输入信息(指令 及 选项 )

    if (fgets(cmd_line, sizeof cmd_line, stdin) == nullptr)
      continue;  // 如果从输入流中获取数据失败则进行下一次循环 该次循环不算
    
    // debug
    // for (auto it : cmd_line) {
    //   cout << it << " ";
    // }
    // cout << strlen(cmd_line) << endl;


    if (strlen(cmd_line) > 1)
    //在该处应加入一次判断 判断用户输入字符是否大于1 若是不大于而执行说明大部分的情况可能是单纯输入了一个回车
    //当只输入回车时则会出现因为修改cmd_line[strlen(cmd_line)-1]位置而导致越界错误
      cmd_line[strlen(cmd_line) - 1] =
          '\0';  // 由于输入换行后该缓冲区将会存储一个换行并且进行打印
                 // 故需要将改缓冲区的换行修正为'\0'
    // TODO
    else
      continue;

    // cout << "echo :" << cmd_line << endl;// --debug 用于打印是否正确

    // 3.将缓冲区内的字符串进行分块 即命令行字符串解析工作
    g_argv[0] =
        strtok(cmd_line, SEP);  // 第一次调用strtok函数的时候需要传入原始字符串
    int index = 1;
    while ((g_argv[index++] = strtok(nullptr, SEP))) {
      ;  // 第二次调用时若是需要分割的是原始字符串则传入空null
    }

    // 5.判断遇到环境变量
    if (strcmp("export", g_argv[0]) == 0 && g_argv[1] != nullptr) {
      strcpy(g_myval, g_argv[1]);
      int ret = putenv(g_myval);
      if (ret == 0) {
        cout << g_argv[1] << "  export success" << endl;
        continue;
      }   //验证父进程当中的环境变量将遗传给子进程
    }

    /*
      //用于debug
      for (index = 0; g_argv[index]; ++index) {
        printf("g_argv[%d] : %s\n", index, g_argv[index]);
      }
    */

    // 4.用于cd命令 需要在父进程阶段进行
    if (strcmp("cd", g_argv[0]) == 0) {
      // if (g_argv[1] != nullptr && chdir(g_argv[1]) != 0) {
      //   cerr << "chdir failed: " << strerror(errno) << endl;
      // }

      if (g_argv[1] != nullptr) chdir(g_argv[1]);
      continue;
    }

    // 5.创建进程 子进程执行指令 父进程等待分析指令
    pid_t id = fork();
    if (id == 0) {
      // 子进程
      cout << "子进程进行执行" << endl;
      execvp(g_argv[0], g_argv);
      exit(1);

    } else if (id > 0) {
      // 父进程
      int status = 0;

      // cout << g_argv[1] << endl;
      waitpid(-1, &status, 0);
      if (WIFEXITED(status)) {
        cout << "WEXITSTATUS:" << WEXITSTATUS(status) << endl;
      }
    } else {
      exit(-1);
    }
  }

  return 0;
}
